import yaml
import os
import os.path
import io
import re

import docx
import docx.oxml
import docx.oxml.ns
import docx.shared
from openpyxl import load_workbook

import config
import utils
import utils.file
import utils.string
import utils.plantuml

component_map = {}


def read_yaml_file(path: str) -> dict:
    with open(path, 'r', encoding='utf8') as f:
        my_dict = yaml.load(f, Loader=yaml.Loader)
    return my_dict


def find_yml_files(directory):
    yml_files = []
    pattern = re.compile(r'^\d+\.(yml|yaml)$')
    # 使用 os.walk 递归遍历目录
    for root, dirs, files in os.walk(directory):
        # print(root, dirs, files)
        for file in files:
            if pattern.match(file):
                # 拼接完整的文件路径
                yml_files.append(os.path.join(root, file))
    # 自定义排序键函数，从文件名中提取数字部分进行排序
    print(yml_files)
    yml_files = sorted(yml_files, key=lambda x: int(re.search(r'\d+', x).group()))
    return yml_files


def get_component_map(excel_file_path: str) -> dict:
    wb = load_workbook(excel_file_path)
    ws = wb.active  # 获取活动工作表
    
    component_map = {}
    # 遍历每一行
    for row in ws.iter_rows(min_row=1, max_row=ws.max_row, min_col=1, max_col=3):  # 假设从第2行、第1列开始，到C列结束
        a_cell = row[0]  # A列
        b_cell = row[1]  # B列
        c_cell = row[2]  # C列
        
        component_map[c_cell.value] = a_cell.value
    return component_map


class SoftwareUnitDesign:
    def __init__(self, field, custom):
        super().__init__()
        self.__index = 0
        self.__doc = docx.Document()
        section = self.__doc.sections[0]
        
        section.page_width = docx.shared.Cm(21)
        section.page_height = docx.shared.Cm(29.7)
        
        section.top_margin = docx.shared.Cm(2.54)
        section.bottom_margin = docx.shared.Cm(2.54)
        section.left_margin = docx.shared.Cm(2.25)
        section.right_margin = docx.shared.Cm(2)
        
        self.__field = field
        self.__custom = custom
    
    def __table_init(self):
        p = self.__doc.add_paragraph()
        table = self.__doc.add_table(1, 2)
        table.style = "Table Grid"
        table.autofit = False
        # table.rows[0].height = docx.shared.Cm(30)
        table.columns[0].width = docx.shared.Cm(2.99)
        table.columns[1].width = docx.shared.Cm(13.49)
        return table
    
    @staticmethod
    def __set_text(cell, text: str, west_font: str = 'Times New Roman', east_asia_font: str = '宋体', size: int = 10,
                   bold=False):
        p = cell.paragraphs[0]
        run = p.add_run(text)
        run.font.name = west_font
        run.font.size = docx.shared.Pt(size)
        run.element.rPr.rFonts.set(docx.oxml.ns.qn('w:eastAsia'), east_asia_font)
        run.font.bold = bold
    
    @staticmethod
    def __to_string(param):
        print(param)
        match str(type(param)):
            case "<class 'NoneType'>":
                ret = 'None'
            case "<class 'list'>":
                # TODO: 数组保留顺序去重
                ret = '\n'.join(list(set(param)))
            case "<class 'dict'>":
                ret = '\n'.join([f'{key}: {SoftwareUnitDesign.__to_string(value)}' for key, value in param.items()])
            case _:
                ret = param
        return ret
    
    @staticmethod
    def __set_table_header(table, tag, definition):
        SoftwareUnitDesign.__set_text(table.cell(0, 0), tag, 'Calibri', '仿宋', 11, bold=True)
        SoftwareUnitDesign.__set_text(table.cell(0, 1), definition, 'Times New Roman', '仿宋', 10, bold=True)
        
        table.cell(0, 0)._tc.get_or_add_tcPr().append(
            docx.oxml.parse_xml(rf'<w:shd {docx.oxml.ns.nsdecls('w')} w:fill="C5E0B4"/>'))
        table.cell(0, 1)._tc.get_or_add_tcPr().append(
            docx.oxml.parse_xml(rf'<w:shd {docx.oxml.ns.nsdecls('w')} w:fill="C5E0B4"/>'))
    
    @staticmethod
    def __set_implementation(table, file_path):
        puml_code = utils.file.read_text_file(os.path.join(config.repo_root, file_path))
        enlarge_scale = 2
        puml_code = utils.string.insert_line_after(original_text=puml_code, target_line='@startuml', new_line=f'scale {enlarge_scale}')
        png_data = utils.plantuml.puml_to_png(puml_code=puml_code, server_ip=config.plantuml_picoweb_server['ip'], server_port=config.plantuml_picoweb_server['port'])
        
        memory_file = io.BytesIO()  # 创建一个 BytesIO 对象
        memory_file.write(png_data)  # 向内存中的文件写入二进制数据
        
        cell = table.cell(len(table.rows) - 1, 1)
        p = cell.paragraphs[0]
        r = p.add_run()
        
        # 图片大小优化
        pic = r.add_picture(memory_file)
        pic.width = int(pic.width * enlarge_scale / 6)
        pic.height = int(pic.height * enlarge_scale / 6)
        
        pic_max_width_cm = 13
        if pic.width.cm > pic_max_width_cm:
            r.clear()
            pic = r.add_picture(memory_file, width=docx.shared.Cm(pic_max_width_cm))
        
        pic_max_height_cm = 13
        if pic.height.cm > pic_max_height_cm:
            r.clear()
            pic = r.add_picture(memory_file, height=docx.shared.Cm(pic_max_height_cm))
        
        p2 = cell.add_paragraph()
        
        # 添加一个 Run，并设置字体
        run = p2.add_run('（点击放大 Click to enlarge）')
        
        west_font: str = 'Arial'
        east_asia_font: str = '仿宋'
        size: int = 10
        bold = False
        run.font.name = west_font
        run.font.size = docx.shared.Pt(size)
        run.element.rPr.rFonts.set(docx.oxml.ns.qn('w:eastAsia'), east_asia_font)
        run.font.bold = bold
    
    def add_function_doc(self, my_dict: dict):
        if self.__custom not in my_dict.keys():
            print('skip')
            return
        table = self.__table_init()
        # tag = my_dict[self.__custom]['Index']
        self.__index += 1
        tag = f"S02E02C01-I{self.__index:04}"
        self.__set_table_header(table, tag, my_dict['Function Definition'])
        for index, item in self.__field.items():
            # print(index, item)
            table.add_row()
            cell = table.cell(len(table.rows) - 1, 0)
            self.__set_text(cell, item, 'Arial', '仿宋', 10)
            
            if index == 'Implementation':
                self.__set_implementation(table, self.__to_string(my_dict[self.__custom][index]))
            elif index == 'Called by':
                a_list = my_dict[self.__custom][index]
                if a_list is not None and len(a_list) > 0:
                    b_list = [component_map[a] if a in component_map.keys() else a for a in a_list]
                else:
                    b_list = []
                value = self.__to_string(b_list)
                self.__set_text(table.cell(len(table.rows) - 1, 1), value, 'Arial', '仿宋', 10)
            else:
                if index in my_dict.keys():
                    value = self.__to_string(my_dict[index])
                elif index in my_dict[self.__custom].keys():
                    value = self.__to_string(my_dict[self.__custom][index])
                
                self.__set_text(table.cell(len(table.rows) - 1, 1), value, 'Arial', '仿宋', 10)
    
    def save(self, path: str = 'default_name.docx'):
        if path != '' and path is not None:
            self.__doc.save(path)
        else:
            self.__doc.save('default_name.docx')
    
    def __del__(self):
        self.save('backup.docx')
        print('Deconstructor')


if __name__ == '__main__':
    field = read_yaml_file(config.custom_config)
    
    ymls = find_yml_files(config.generate_path)
    print(ymls)
    
    component_map = get_component_map(config.component_map_file)
    
    doc = SoftwareUnitDesign(field=field, custom=config.custom_field_name)
    
    for yml in ymls:
        data = read_yaml_file(yml)
        doc.add_function_doc(data)
    
    doc.save(config.output_file)
